#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define TIMER_CNT		1		/* 设备号个数 */
#define TIMER_NAME		"timer"	/* 名字 */
#define CLOSE_CMD		_IO(0XEF, 0X1) /* 关闭定时器 */
#define OPEN_CMD		 _IO(0XEF, 0X2) /* 打开定时器 */
#define SETPERIOD_CMD	 _IO(0XEF, 0X3) /* 设置周期 */
#define LEDOFF			0
#define LEDON			1
/* 设备结构体 */
struct timer_dev{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
	struct device_node	*nd; /* 设备节点 */
	int led_gpio;			/* led所使用的GPIO编号		*/
	struct timer_list timer;	/* 定时器 */
	int period;				/* 周期，单位为ms */
    spinlock_t lock;		/* 自旋锁 */
};

struct timer_dev timer;		/* 设备结构体 */

static int led_init(void)
{
    int ret = 0;
    timer.nd = of_find_node_by_path("/gpioled");
    timer.led_gpio = of_get_named_gpio(timer.nd, "led-gpios", 0);
    if(timer.led_gpio < 0) {
        printk("can't get led-gpio\n");
        return -EFAULT;
    }
    printk("led-gpio num = %d\r\n", timer.led_gpio);
    ret = gpio_request(timer.led_gpio, "led-gpio");
    if(ret < 0) {
        printk("can't request the led-gpio\n");
        return -EFAULT;
    }
    ret = gpio_direction_output(timer.led_gpio, 1);
    if(ret < 0) {
        printk("can't set gpio");
    }
    return 0;
}
static int timer_open(struct inode *inode,struct file *filp)
{
    int ret = 0;
	filp->private_data = &timer; /* 设置私有数据 */
    timer.period = 1000;
    ret = led_init();
    if(ret < 0)
    {
        return -EFAULT;
    }
    return 0;
}
static int timer_release(struct inode *inode,struct file *filp)
{
	return 0;
}
static long timer_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
	struct timer_dev *dev = (struct timer_dev *)filp->private_data;
    int timerperiod = 0;
    unsigned long flags;
    int ret = 0;
    switch(cmd) {
        case CLOSE_CMD:             // 关闭定时器
            del_timer(&dev->timer);
            break;
        case OPEN_CMD:          // 打开定时器
            spin_lock_irqsave(&dev->lock, flags);
            timerperiod = dev->period;
            spin_unlock_irqrestore(&dev->lock, flags);
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timerperiod));
            break;
        case SETPERIOD_CMD:     // 设置周期
            spin_lock_irqsave(&dev->lock, flags);
            dev->period = arg;
            spin_unlock_irqrestore(&dev->lock, flags);
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->period));
            break;
        default:
            ret = -EINVAL;
            break;
    }
    return 0;
}

static struct file_operations timer_fops = {
    .owner = THIS_MODULE,
    .open = timer_open,
    .unlocked_ioctl = timer_ioctl,
    .release = timer_release,

};

void timer_function(unsigned long arg)
{
    struct timer_dev *dev = (struct timer_dev *)arg;
    static int sta = 0;
    unsigned long flags;
    int timerperiod = 0;
    sta = !sta;
    gpio_set_value(dev->led_gpio, sta);
    //重启定时器
    spin_lock_irqsave(&dev->lock, flags);
    timerperiod = dev->period;
    spin_unlock_irqrestore(&dev->lock, flags);
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->period));
}
static int __init timer_init(void)
{
	int ret = 0;
    /* 初始化自旋锁 */
    spin_lock_init(&timer.lock);    
    /* 注册字符设备驱动 */
	/* 1、创建设备号 */
	if (timer.major) {		/*  定义了设备号 */
		timer.devid = MKDEV(timer.major, 0);
		ret = register_chrdev_region(timer.devid, TIMER_CNT, TIMER_NAME);   
	} else {						/* 没有定义设备号 */
		ret = alloc_chrdev_region(&timer.devid, 0, TIMER_CNT, TIMER_NAME);	/* 申请设备号 */
		timer.major = MAJOR(timer.devid);	/* 获取分配号的主设备号 */
		timer.minor = MINOR(timer.devid);	/* 获取分配号的次设备号 */
	}
	if(ret < 0) {
		printk("timer chrdev_region fail\r\n");
		return -EIO;
	}
	printk("timer major=%d,minor=%d\r\n",timer.major, timer.minor);	/* 打印设备号信息 */

	/* 2、初始化cdev */
	timer.cdev.owner = THIS_MODULE;
    cdev_init(&timer.cdev, &timer_fops);

    /* 3、添加一个cdev */
    ret = cdev_add(&timer.cdev, timer.devid, TIMER_CNT);
    if(ret < 0) {
        goto fail_cdev;
    }
    /* 4、创建类 */
    timer.class = class_create(THIS_MODULE, TIMER_NAME);
    if(IS_ERR(timer.class)) {
        ret = PTR_ERR(timer.class);
    }

    /* 5、创建设备 */
    timer.device = device_create(timer.class, NULL, timer.devid, NULL, TIMER_NAME); 
    if(IS_ERR(timer.device)) {
        ret = PTR_ERR(timer.device);
        goto fail_device;
    }
    /* 初始化定时器 */
    init_timer(&timer.timer);
    timer.timer.function = timer_function;
    timer.timer.data = (unsigned long)&timer;
    return 0;

fail_device:
    class_destroy(timer.class);
    cdev_del(&timer.cdev);/*  删除cdev */
fail_cdev:
    unregister_chrdev_region(timer.devid, TIMER_CNT);
    return ret;

}
static void __exit timer_exit(void)
{
    /* 注销字符设备驱动 */
    printk("timer_exit\r\n");
    del_timer(&timer.timer);
    gpio_free(timer.led_gpio);
    cdev_del(&timer.cdev);/*  删除cdev */
    unregister_chrdev_region(timer.devid, TIMER_CNT); /* 注销设备号 */
    device_destroy(timer.class, timer.devid);
    class_destroy(timer.class);
}

module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WXY");